package org.example.rubbish.websocket;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import jakarta.websocket.*;
import jakarta.websocket.server.PathParam;
import jakarta.websocket.server.ServerEndpoint;
import lombok.extern.slf4j.Slf4j;
import org.example.rubbish.config.CorsServerEndpointConfigurator;
import org.example.rubbish.config.SpringContextHolder;
import org.example.rubbish.dto.AlarmDTO;
import org.example.rubbish.dto.WebSocketMessage;
import org.example.rubbish.entity.Site;
import org.example.rubbish.service.AlarmService;
import org.example.rubbish.service.SiteService;
import org.example.rubbish.service.TerminalService;
import org.example.rubbish.service.UnifiedWebSocketService;
import org.example.rubbish.util.JwtUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Slf4j
@Component
@ServerEndpoint(value = "/websocket/terminal/{terminalId}",
        configurator = CorsServerEndpointConfigurator.class)
public class TerminalWebSocketServer {

    // 存储终端会话
    private static final ConcurrentHashMap<String, Session> sessionMap = new ConcurrentHashMap<>();
    // 存储终端信息
    private static final ConcurrentHashMap<String, TerminalInfo> terminalInfoMap = new ConcurrentHashMap<>();
    private static final ObjectMapper objectMapper = new ObjectMapper()
            .registerModule(new JavaTimeModule())  // ✅ 支持 Java 8 日期时间
            .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);  // ✅ 日期格式化为字符串

    // 静态服务引用（通过setter注入）
    private static JwtUtils jwtUtils;
    private static TerminalService terminalService;
    private static AlarmService alarmService;
    private static SiteService siteService;
    private static UnifiedWebSocketService unifiedWebSocketService;
    private static UnifiedWebSocketService getUnifiedWebSocketService() {
        if (unifiedWebSocketService == null) {
            unifiedWebSocketService = SpringContextHolder.getBean(UnifiedWebSocketService.class);
        }
        return unifiedWebSocketService;
    }
    @Autowired
    public void setJwtUtils(JwtUtils jwtUtils) {
        TerminalWebSocketServer.jwtUtils = jwtUtils;
    }

    @Autowired
    public void setTerminalService(TerminalService terminalService) {
        TerminalWebSocketServer.terminalService = terminalService;
    }
    public void setUnifiedWebSocketService(UnifiedWebSocketService unifiedWebSocketService) {
        TerminalWebSocketServer.unifiedWebSocketService = unifiedWebSocketService;
    }

    @Autowired
    public void setAlarmService(AlarmService alarmService) {
        TerminalWebSocketServer.alarmService = alarmService;
    }

    @Autowired
    public void setSiteService(SiteService siteService) {
        TerminalWebSocketServer.siteService = siteService;
    }

    /**
     * WebSocket连接建立时调用
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("terminalId") String terminalId) {
        try {
            // 1. 验证终端ID（从sites表验证）
            if (!validateTerminalId(terminalId)) {
                session.close(new CloseReason(CloseReason.CloseCodes.VIOLATED_POLICY, "无效的终端ID"));
                return;
            }

            // 2. 获取站点信息
            Site site = siteService.getSiteByTerminalId(terminalId);
            if (site == null) {
                session.close(new CloseReason(CloseReason.CloseCodes.VIOLATED_POLICY, "站点不存在"));
                return;
            }

            // 3. 存储终端信息
            TerminalInfo terminalInfo = new TerminalInfo(
                    terminalId,
                    site.getStationName(),
                    site.getStationCode(),
                    site.getAddress(),
                    session.getId()
            );
            terminalInfoMap.put(terminalId, terminalInfo);
            sessionMap.put(terminalId, session);

            // 4. 发送连接成功消息
            sendMessage(session, new WebSocketMessage("SYSTEM",
                    String.format("终端连接成功 - %s(%s)", site.getStationName(), site.getStationCode())));

            // 5. 连接成功后自动发送最近未确认的报警
            sendRecentUnacknowledgedAlarms(session);

            // 6. 广播连接数更新
            broadcastConnectionCount();

        } catch (Exception e) {
            try {
                session.close(new CloseReason(CloseReason.CloseCodes.UNEXPECTED_CONDITION, "连接异常"));
            } catch (IOException ex) {
            }
        }
    }

    /**
     * 验证终端ID是否有效
     */
    private boolean validateTerminalId(String terminalId) {
        if (terminalId == null || terminalId.trim().isEmpty()) {
            return false;
        }

        return true;
    }

    @OnClose
    public void onClose(Session session, @PathParam("terminalId") String terminalId) {
        sessionMap.remove(terminalId);
        terminalInfoMap.remove(terminalId);
        broadcastConnectionCount();
    }

    @OnMessage
    public void onMessage(String message, Session session, @PathParam("terminalId") String terminalId) {
        try {
            System.out.println("【TerminalWebSocketServer】收到消息，terminalId=" + terminalId);
            WebSocketMessage webSocketMessage = objectMapper.readValue(message, WebSocketMessage.class);
            System.out.println("【TerminalWebSocketServer】解析成功，type=" + webSocketMessage.getType());
            System.out.println("【TerminalWebSocketServer】收到原始消息: " + message);
            // 处理心跳消息
            if ("heartbeat".equals(webSocketMessage.getType())) {
                sendMessage(session, new WebSocketMessage("heartbeat", "pong"));
                return;
            }

            // 处理终端发送报警消息
            if ("ALARM".equals(webSocketMessage.getType())) {
                handleTerminalAlarm(webSocketMessage, session, terminalId);
                return;
            }

            // 处理终端确认消息（如果需要终端也能确认报警的话）
            if ("ACKNOWLEDGE".equals(webSocketMessage.getType())) {
                handleTerminalAcknowledge(webSocketMessage, session, terminalId);
                return;
            }

            // 其他不支持的消息类型
            sendMessage(session, new WebSocketMessage("ERROR", "不支持的消息类型"));
            System.out.println("【TerminalWebSocketServer】收到原始消息: " + message);
        } catch (Exception e) {
            sendMessage(session, new WebSocketMessage("ERROR", "消息处理失败"));
        }
    }


    /**
     * 处理终端发送的报警消息
     */
    private void handleTerminalAlarm(WebSocketMessage message, Session session, String terminalId) {
        try {
            Object data = message.getData();
            if (data instanceof java.util.Map) {
                java.util.Map<?, ?> dataMap = (java.util.Map<?, ?>) data;

                // 获取站点信息
                Site site = siteService.getSiteByTerminalId(terminalId);
                if (site == null) {
                    sendMessage(session, new WebSocketMessage("ERROR", "站点信息不存在"));
                    return;
                }

                // 准备扩展数据
                Object extraData = Map.of(
                        "terminalId", terminalId,
                        "stationId", site.getId(),
                        "stationCode", site.getStationCode(),
                        "stationName", site.getStationName(),
                        "address", site.getAddress(),
                        "latitude", site.getLatitude(),
                        "longitude", site.getLongitude(),
                        "timestamp", System.currentTimeMillis()
                );

                // 安全地获取数据并处理类型
                String title = dataMap.get("title") != null ? dataMap.get("title").toString() : "";
                String content = dataMap.get("content") != null ? dataMap.get("content").toString() : "";
                String level = dataMap.get("level") != null ? dataMap.get("level").toString() : "MEDIUM";
                String type = dataMap.get("type") != null ? dataMap.get("type").toString() : "TERMINAL";

                // 创建报警DTO - 使用构造函数
                AlarmDTO alarmDTO = new AlarmDTO(
                        "TERMINAL_" + site.getStationCode() + "_" + System.currentTimeMillis(), // alarmId
                        title, // title
                        content, // content
                        "TERMINAL_" + site.getStationCode(), // source
                        level, // level
                        type, // type
                        extraData // extraData
                );

                // 处理报警（保存到数据库并广播）
                alarmService.processNewAlarm(alarmDTO);
                getUnifiedWebSocketService().broadcastAlarm(alarmDTO);
                // 返回成功消息给终端
                sendMessage(session, new WebSocketMessage("ALARM_RESULT",
                        Map.of("success", true,
                                "message", "报警发送成功",
                                "alarmId", alarmDTO.getAlarmId(),
                                "stationName", site.getStationName(),
                                "stationCode", site.getStationCode())));

            } else {
                sendMessage(session, new WebSocketMessage("ERROR", "报警数据格式错误"));
            }
        } catch (Exception e) {
            sendMessage(session, new WebSocketMessage("ERROR", "处理报警消息失败"));
        }
    }
    /**
     * 处理终端报警确认
     */
    private void handleTerminalAcknowledge(WebSocketMessage message, Session session, String terminalId) {
        try {
            Object data = message.getData();
            if (data instanceof java.util.Map) {
                java.util.Map<?, ?> dataMap = (java.util.Map<?, ?>) data;
                String alarmId = (String) dataMap.get("alarmId");

                if (alarmId != null && terminalService != null) {
                    // 获取站点信息
                    Site site = siteService.getSiteByTerminalId(terminalId);
                    String acknowledgedBy = site != null ?
                            String.format("终端设备_%s(%s)", site.getStationName(), site.getStationCode()) :
                            "终端设备_" + terminalId;

                    // 调用报警确认服务
                    boolean success = terminalService.acknowledgeAlarm(alarmId, acknowledgedBy);

                    if (success) {
                        sendMessage(session, new WebSocketMessage("ACKNOWLEDGE_RESULT", "报警确认成功"));
                        // 广播确认消息给所有客户端
                        broadcastAcknowledgment(alarmId, acknowledgedBy);
                    } else {
                        sendMessage(session, new WebSocketMessage("ACKNOWLEDGE_RESULT", "报警确认失败"));
                    }
                } else {
                    sendMessage(session, new WebSocketMessage("ERROR", "报警ID不能为空"));
                }
            }
        } catch (Exception e) {

            sendMessage(session, new WebSocketMessage("ERROR", "处理确认消息失败"));
        }
    }

    /**
     * 处理确认消息（从管理端广播过来的确认消息）
     */
    public void handleAcknowledgmentFromManagement(String alarmId, String acknowledgedBy) {
        try {
            // 查找这个报警对应的终端
            String terminalId = findTerminalIdByAlarmId(alarmId);
            if (terminalId != null) {
                Session session = sessionMap.get(terminalId);
                if (session != null && session.isOpen()) {
                    // 发送确认消息给对应的终端
                    WebSocketMessage message = new WebSocketMessage("ACKNOWLEDGMENT",
                            Map.of("alarmId", alarmId,
                                    "acknowledgedBy", acknowledgedBy,
                                    "acknowledgedTime", System.currentTimeMillis(),
                                    "message", "报警已被确认处理"));
                    sendMessage(session, message);
                }
            }
        } catch (Exception e) {
        }
    }

    /**
     * 根据报警ID查找对应的终端ID
     */
    private String findTerminalIdByAlarmId(String alarmId) {
        // 这里需要从报警ID中提取终端信息
        // 假设报警ID格式为：TERMINAL_{stationCode}_{timestamp}
        if (alarmId.startsWith("TERMINAL_")) {
            String[] parts = alarmId.split("_");
            if (parts.length >= 2) {
                String stationCode = parts[1];
                // 根据站点代码查找对应的终端ID
                for (TerminalInfo terminalInfo : terminalInfoMap.values()) {
                    if (stationCode.equals(terminalInfo.getStationCode())) {
                        return terminalInfo.getTerminalId();
                    }
                }
            }
        }
        return null;
    }

    /**
     * 连接成功后发送最近未确认报警
     */
    private void sendRecentUnacknowledgedAlarms(Session session) {
        try {
            // 获取最近24小时的未确认报警
            List<AlarmDTO> unacknowledgedAlarms = alarmService.getRecentUnacknowledgedAlarms(24);

            if (!unacknowledgedAlarms.isEmpty()) {
                sendDatabaseAlarmsToSession(session, unacknowledgedAlarms);
            }
        } catch (Exception e) {
            sendMessage(session, new WebSocketMessage("ERROR", "加载历史报警失败"));
        }
    }

    /**
     * 向指定会话发送数据库报警列表
     */
    private void sendDatabaseAlarmsToSession(Session session, List<AlarmDTO> alarms) {
        try {
            WebSocketMessage message = new WebSocketMessage("DATABASE_ALARM_LIST", alarms);
            sendMessage(session, message);
        } catch (Exception e) {
        }
    }

    @OnError
    public void onError(Session session, Throwable error, @PathParam("terminalId") String terminalId) {

    }

    /**
     * 向指定会话发送消息
     */
    public void sendMessage(Session session, WebSocketMessage message) {
        try {
            if (session != null && session.isOpen()) {
                synchronized (session) {
                    String messageJson = objectMapper.writeValueAsString(message);
                    session.getBasicRemote().sendText(messageJson);
                }
            }
        } catch (IOException e) {
        }
    }

    /**
     * 向特定终端发送报警
     */
    public void sendAlarmToTerminal(String terminalId, AlarmDTO alarmDTO) {
        Session session = sessionMap.get(terminalId);
        if (session != null && session.isOpen()) {
            WebSocketMessage message = new WebSocketMessage("ALARM", alarmDTO);
            sendMessage(session, message);
        }
    }

    /**
     * 广播报警给所有终端用户
     */
    public void broadcastAlarm(AlarmDTO alarmDTO) {
        WebSocketMessage message = new WebSocketMessage("ALARM", alarmDTO);
        broadcastMessage(message);
    }

    /**
     * 广播确认消息给所有终端用户
     */
    public void broadcastAcknowledgment(String alarmId, String acknowledgedBy) {
        // 调用处理确认消息的方法，将确认消息发送给对应的终端
        handleAcknowledgmentFromManagement(alarmId, acknowledgedBy);
    }

    /**
     * 广播连接数统计
     */
    private void broadcastConnectionCount() {
        WebSocketMessage message = new WebSocketMessage("STATS",
                new StatsMessage(sessionMap.size(), "terminal"));
        broadcastMessage(message);
    }

    /**
     * 广播消息给所有连接的客户端
     */
    private void broadcastMessage(WebSocketMessage message) {
        String messageJson;
        try {
            messageJson = objectMapper.writeValueAsString(message);
            System.out.println(">>> 广播原始消息内容: " + messageJson);
        } catch (Exception e) {
            return;
        }

        sessionMap.forEach((sessionId, session) -> {
            try {
                if (session != null && session.isOpen()) { // ← 加这个判断
                    synchronized (session) {
                        session.getBasicRemote().sendText(messageJson);
                        System.out.println(">>> 已发送给会话: " + sessionId);
                    }
                } else {
                    System.out.println(">>> 会话已关闭，跳过: " + sessionId);
                    sessionMap.remove(sessionId); // ← 及时清理
                }
            } catch (IOException e) {
                System.out.println(">>> 发送失败，移除会话: " + sessionId);
                sessionMap.remove(sessionId); // ← 异常时也清理
            }
        });

    }

    public int getConnectionCount() {
        return sessionMap.size();
    }

    // 获取当前在线的所有终端ID
    public java.util.Set<String> getOnlineTerminals() {
        return sessionMap.keySet();
    }

    // 内部消息类
    private static class StatsMessage {
        private int connectionCount;
        private String clientType;
        private long timestamp;

        public StatsMessage() {}

        public StatsMessage(int connectionCount, String clientType) {
            this.connectionCount = connectionCount;
            this.clientType = clientType;
            this.timestamp = System.currentTimeMillis();
        }

        public int getConnectionCount() { return connectionCount; }
        public void setConnectionCount(int connectionCount) { this.connectionCount = connectionCount; }
        public String getClientType() { return clientType; }
        public void setClientType(String clientType) { this.clientType = clientType; }
        public long getTimestamp() { return timestamp; }
        public void setTimestamp(long timestamp) { this.timestamp = timestamp; }
    }

    /**
     * 终端信息类
     */
    public static class TerminalInfo {
        private String terminalId;
        private String stationName;
        private String stationCode;
        private String address;
        private String sessionId;
        private long connectTime;

        public TerminalInfo() {
            this.connectTime = System.currentTimeMillis();
        }

        public TerminalInfo(String terminalId, String stationName, String stationCode, String address, String sessionId) {
            this.terminalId = terminalId;
            this.stationName = stationName;
            this.stationCode = stationCode;
            this.address = address;
            this.sessionId = sessionId;
            this.connectTime = System.currentTimeMillis();
        }

        // getter and setter
        public String getTerminalId() { return terminalId; }
        public void setTerminalId(String terminalId) { this.terminalId = terminalId; }
        public String getStationName() { return stationName; }
        public void setStationName(String stationName) { this.stationName = stationName; }
        public String getStationCode() { return stationCode; }
        public void setStationCode(String stationCode) { this.stationCode = stationCode; }
        public String getAddress() { return address; }
        public void setAddress(String address) { this.address = address; }
        public String getSessionId() { return sessionId; }
        public void setSessionId(String sessionId) { this.sessionId = sessionId; }
        public long getConnectTime() { return connectTime; }
        public void setConnectTime(long connectTime) { this.connectTime = connectTime; }
    }
}